//-
// ==========================================================================
// Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors.  All 
// rights reserved.
//
// The coded instructions, statements, computer programs, and/or related 
// material (collectively the "Data") in these files contain unpublished 
// information proprietary to Autodesk, Inc. ("Autodesk") and/or its 
// licensors, which is protected by U.S. and Canadian federal copyright 
// law and by international treaties.
//
// The Data is provided for use exclusively by You. You have the right 
// to use, modify, and incorporate this Data into other products for 
// purposes authorized by the Autodesk software license agreement, 
// without fee.
//
// The copyright notices in the Software and this entire statement, 
// including the above license grant, this restriction and the 
// following disclaimer, must be included in all copies of the 
// Software, in whole or in part, and all derivative works of 
// the Software, unless such copies or derivative works are solely 
// in the form of machine-executable object code generated by a 
// source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. 
// AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED 
// WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF 
// NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 
// PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR 
// TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS 
// BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, 
// DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK 
// AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY 
// OR PROBABILITY OF SUCH DAMAGES.
//
// ==========================================================================
//+

///////////////////////////////////////////////////////////////////////////////
//
// apiMeshData.cpp
//
///////////////////////////////////////////////////////////////////////////////

#include <maya/MIOStream.h>
#include <apiMeshData.h>
#include <apiMeshIterator.h>
#include <api_macros.h>

#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MArgList.h>

//////////////////////////////////////////////////////////////////////

// Ascii file IO defines
//
#define kDblQteChar				"\""
#define kSpaceChar				"	"
#define kWrapString				"\n\t\t"
#define kVertexKeyword			"v"
#define kNormalKeyword			"vn"
#define kTextureKeyword			"vt"
#define kFaceKeyword			"face"

//////////////////////////////////////////////////////////////////////

const MTypeId apiMeshData::id( 0x80777 );
const MString apiMeshData::typeName( "apiMeshData" );

apiMeshData::apiMeshData() : fGeometry( NULL )
{
	fGeometry = new apiMeshGeom;
}

apiMeshData::~apiMeshData()
{
	if ( NULL != fGeometry ) {
		delete fGeometry;
		fGeometry = NULL;
	}
}

/* override */
MStatus apiMeshData::readASCII( const MArgList& argList, unsigned& index )
//
// Description
//    ASCII file input method.
//
{
	if ( ! readVerticesASCII(argList,index) ) {
		return MS::kFailure;
	}
	else if ( ! readNormalsASCII(argList,++index) ) {
		return MS::kFailure;
	}
	else if ( ! readFacesASCII(argList,++index) ) {
		return MS::kFailure;
	}

	return MS::kSuccess;
}

/* override */
MStatus apiMeshData::readBinary( istream& /*in*/, unsigned /*length*/ )
//
// Description
//     NOT IMPLEMENTED
//
{
	return MS::kSuccess;
}

/* override */
MStatus apiMeshData::writeASCII( ostream& out )
//
// Description
//    ASCII file output method.
//
{
	if ( ! writeVerticesASCII(out) ) {
		return MS::kFailure;
	}
	else if ( ! writeNormalsASCII(out) ) {
		return MS::kFailure;
	}
	else if ( ! writeFacesASCII(out) ) {
		return MS::kFailure;
	}

	return MS::kSuccess;
}

/* override */
MStatus apiMeshData::writeBinary( ostream& /*out*/ )
//
// Description
//    NOT IMPLEMENTED
//
{
	return MS::kSuccess;
}

/* override */
void apiMeshData::copy ( const MPxData& other )
{
	*fGeometry = *(((const apiMeshData &)other).fGeometry);
}

/* override */
MTypeId apiMeshData::typeId() const
//
// Description
//    Binary tag used to identify this kind of data
//
{
	return apiMeshData::id;
}

/* override */
MString apiMeshData::name() const
//
// Description
//    String name used to identify this kind of data
//
{
	return apiMeshData::typeName;
}

void * apiMeshData::creator()
{
	return new apiMeshData;
}

/* override */
MPxGeometryIterator* apiMeshData::iterator( MObjectArray & componentList,
											MObject & component,
											bool useComponents)
//
// Description
//
{
	apiMeshGeomIterator * result = NULL;
	if ( useComponents ) {
		result = new apiMeshGeomIterator( fGeometry, componentList );
	}
	else {
		result = new apiMeshGeomIterator( fGeometry, component );
	}
	return result;
}

/* override */
MPxGeometryIterator* apiMeshData::iterator( MObjectArray & componentList,
											MObject & component,
											bool useComponents,
											bool /*world*/) const
//
// Description
//
{
	apiMeshGeomIterator * result = NULL;
	if ( useComponents ) {
		result = new apiMeshGeomIterator( fGeometry, componentList );
	}
	else {
		result = new apiMeshGeomIterator( fGeometry, component );
	}
	return result;
}

/* override */
bool apiMeshData::updateCompleteVertexGroup( MObject & component ) const
//
// Description
//     Make sure complete vertex group data is up-to-date.
//     Returns true if the component was updated, false if it was already ok.
//
//     This is used by deformers when deforming the "whole" object and
//     not just selected components.
//
{
	MStatus stat;
	MFnSingleIndexedComponent fnComponent( component, &stat );

	// Make sure there is non-null geometry and that the component
	// is "complete". A "complete" component represents every 
	// vertex in the shape.
	//
	if ( stat && (NULL != fGeometry) && (fnComponent.isComplete()) ) {
	
		int maxVerts ;
		fnComponent.getCompleteData( maxVerts );
		int numVertices = fGeometry->vertices.length();

		if ( (numVertices > 0) && (maxVerts != numVertices) ) {
			// Set the component to be complete, i.e. the elements in
			// the component will be [0:numVertices-1]
			//
			fnComponent.setCompleteData( numVertices );
			return true;
		}
	}

	return false;
}

//////////////////////////////////////////////////////////////////////

MStatus apiMeshData::writeVerticesASCII( ostream& out )
{
	MPoint vertex;
	int vertexCount = fGeometry->vertices.length();

	out << "\n";
	out << kWrapString;
	out << kDblQteChar << kVertexKeyword << kDblQteChar
		<< kSpaceChar << vertexCount;

	for ( int i=0; i<vertexCount; i++ ) {
		vertex = fGeometry->vertices[i];
		out << kWrapString;
		out << vertex[0] << kSpaceChar;
		out << vertex[1] << kSpaceChar;
		out << vertex[2];
	}

	return MS::kSuccess;
}

MStatus apiMeshData::writeNormalsASCII( ostream& out )
{
	MVector normal;
	int normalCount = fGeometry->normals.length();

	out << "\n";
	out << kWrapString;
	out << kDblQteChar << kNormalKeyword << kDblQteChar
		<< kSpaceChar << normalCount;

	for ( int i=0; i<normalCount; i++ ) {
		normal = fGeometry->normals[i];
		out << kWrapString;
		out << normal[0] << kSpaceChar;
		out << normal[1] << kSpaceChar;
		out << normal[2];
	}

	return MS::kSuccess;
}

MStatus apiMeshData::writeFacesASCII( ostream& out )
{
	int numFaces = fGeometry->face_counts.length();
	int vid = 0;

	for ( int f=0; f<numFaces; f++ )
	{
		int faceVertexCount = fGeometry->face_counts[f];
		out << "\n";
		out << kWrapString;
		out << kDblQteChar << kFaceKeyword << kDblQteChar
			<< kSpaceChar << faceVertexCount;

		out << kWrapString;
		for ( int v=0; v<faceVertexCount; v++ )
		{
			out << fGeometry->face_connects[vid] << kSpaceChar;
			vid++;
		}
	}

	return MS::kSuccess;
}

MStatus apiMeshData::readVerticesASCII( const MArgList& argList,
										unsigned& index )
{
	MStatus result;
	MString geomStr;
	MPoint vertex;
	int vertexCount = 0;

	result = argList.get( index, geomStr );

	if ( result && (geomStr == kVertexKeyword) ) {
		result = argList.get( ++index, vertexCount );
		
		for ( int i=0; i<vertexCount; i++ )
		{
			if ( argList.get( ++index, vertex ) ) {
				fGeometry->vertices.append( vertex );
			}
			else {
				result = MS::kFailure;
			}
		}
	}

	return result;
}

MStatus apiMeshData::readNormalsASCII( const MArgList& argList,
									   unsigned& index )
{
	MStatus result;
	MString geomStr;
	MPoint normal;
	int normalCount = 0;

	result = argList.get( index, geomStr );

	if ( result && (geomStr == kNormalKeyword) ) {
		result = argList.get( ++index, normalCount );
		for ( int i=0; i<normalCount; i++ )
		{
			if ( argList.get( ++index, normal ) ) {
				fGeometry->normals.append( normal );
			}
			else {
				result = MS::kFailure;
			}
		}
	}

	return result;
}

MStatus apiMeshData::readFacesASCII( const MArgList& argList,
									 unsigned& index )
{
	MStatus result = MS::kSuccess;
	MString geomStr;
	int faceCount = 0;
	int vid;

	while( argList.get(index,geomStr) && (geomStr == kFaceKeyword) )
	{
		result = argList.get( ++index, faceCount );
		fGeometry->face_counts.append( faceCount );

		for ( int i=0; i<faceCount; i++ )
		{
			if ( argList.get( ++index, vid ) ) {
				fGeometry->face_connects.append( vid );
			}
			else {
				result = MS::kFailure;
			}
		}
		index++;
	}

	fGeometry->faceCount = fGeometry->face_counts.length();
	return result;
}
